package org.reos;

import java.io.*;
import java.util.TreeMap;

/**
 * Created with IntelliJ IDEA.
 * User: Alexander Rogin
 * Date: 24.12.13
 * Time: 13:57
 * To change this template use File | Settings | File Templates.
 */
public class MemoryParser {
    private static final char STRING_LIMIT_MARKER = 0;
    private static final char ASCII_START_SYMBOL = 0x20;
    private static final char ASCII_END_SYMBOL = 0x7E;
    private final int MAX_STRING_LENGTH = 50;

    private static final int HEX_START_CODE_INDEX = 0;
    private static final int HEX_BYTE_COUNT_START_INDEX = 1;
    private static final int HEX_BYTE_COUNT_END_INDEX = 2;
    private static final int HEX_ADDRESS_START_INDEX = 3;
    private static final int HEX_ADDRESS_END_INDEX = 6;
    private static final int HEX_RECORD_START_INDEX = 7;
    private static final int HEX_RECORD_END_INDEX = 8;
    private static final int HEX_DATA_START_INDEX = 9;

    private static final byte HEX_START_CODE = ':';
    private static final byte HEX_DATA_RECORD_CODE = 0x00;
    private static final byte HEX_EOF_CODE = 0x01;

    private int stringBaseAddress = -1;
    private StringBuilder currentString = null;
    private boolean startNullFound = false;

    TreeMap<Integer, String> stringMap = new TreeMap<Integer, String>();

    public TreeMap<Integer, String> parseHexFile(String fileName) {
        try {
            BufferedReader br = null;
            br = new BufferedReader(new FileReader(new File(fileName)));

            while (true) {
                String str = br.readLine();
                if (str == null) {
                    System.out.println("Unexpected end of file");
                    br.close();
                    return null;
                }
                if (parseHexString(str) == true) {
                    br.close();
                    return stringMap;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        return null;
    }

    private boolean parseHexString(String str) {
        if (str.charAt(HEX_START_CODE_INDEX) != HEX_START_CODE) {
            System.out.println("Start Code (" + HEX_START_CODE + ") required not found");
            return false;
        }
        int recordType = Integer.parseInt(str.substring(HEX_RECORD_START_INDEX, HEX_RECORD_END_INDEX + 1), 16);
        if (recordType == HEX_EOF_CODE) {
            //System.out.println("");
            return true;
        } else if (recordType == HEX_DATA_RECORD_CODE) {
            int dataSize = Integer.parseInt(str.substring(HEX_BYTE_COUNT_START_INDEX, HEX_BYTE_COUNT_END_INDEX + 1), 16);
            int recordBaseAddr = Integer.parseInt(str.substring(HEX_ADDRESS_START_INDEX, HEX_ADDRESS_END_INDEX + 1), 16);
            String recordData = str.substring(HEX_DATA_START_INDEX, HEX_DATA_START_INDEX + dataSize * 2); // Multiply by two because each byte is represented with 2 chars
            int dataStringLen = recordData.length();
            for (int i = 0; i < dataSize; i++) {
                int symbol = Integer.parseInt(recordData.substring(i * 2, i * 2 + 2), 16); // Multiply by two because each byte is represented with 2 chars
                if (symbol == STRING_LIMIT_MARKER) {
                    if (currentString != null) {
                        addStringToMap();
                    } else {
                        startNullFound = true;
                    }
                } else if (startNullFound == true) {
                    if (isCharAscii(symbol) == true) {
                        if (currentString == null) {
                            currentString = new StringBuilder();
                            stringBaseAddress = recordBaseAddr + i;
                        }
                        currentString.append((char) symbol);
                    } else {
                        startNullFound = false;
                        currentString = null;
                    }
                }
            }
        }
        return false;
    }

    private void addStringToMap() {
        if (currentString.length() > MAX_STRING_LENGTH) {
            System.out.println("String is too long");
            return;
        }
        currentString.append('\0');
        stringMap.put(stringBaseAddress, currentString.toString());
        currentString = null;
        startNullFound = false;
    }

    private boolean isCharAscii(int c) {
        if (c >= ASCII_START_SYMBOL && c <= ASCII_END_SYMBOL) {
            return true;
        }
        if (c == 0x09 || c == 0x0A) {
            return true;
        }
        return false;
    }
}
